/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package helloscala.practise.chapter6

import helloscala.practise.ChapterEntry

object Chapter6Entry extends ChapterEntry {
  def runTests: Unit = {
    testEquality
  }

  def testEquality = {
    println(1 == 1)
    println("Jeff" == "Jeff") // ==/!= is used to test the VALUE

    case class C1(val f1: Int)
    println(C1(3) == C1(3))
    println(C1(3) eq C1(3)) // eq/ne is used to test the Reference

    println(Array(5,6,7) == Array(5,6,7))
    println(Array(5,6,7) sameElements Array(5,6,7)) //sameElements is used to test the Array elements
  }

  def testCopy4CaseClass = {
    case class C1(val f1: Int, val f2: String)

    val c1 = C1(5, "Jeff")
    val c1Copy = c1 copy (f2 = "Winnie")

    println(c1)
    println(c1Copy)

    //current scalac version already banned to inherit a case class with
    //another case class, which causes problems
    /*case class C2(val f1: Int, val f2: String, val f3: Double) extends C1(f1, f2)*/
  }

  def testCaseShapes = {
    //use companion object "apply()" to create shapes instances
    val shapesList = List(
      Circle(Point(0.0, 0.0), 1.0),
      Circle(Point(5.0, 2.0), 3.0),
      Rectangle(Point(0.0, 0.0), 2, 5),
      Rectangle(Point(-2.0, -1.0), 4, 3),
      Triangle(Point(0.0, 0.0), Point(1.0, 0.0), Point(0.0, 1.0)))

    val shape1 = shapesList.head
    println("Shape1: " + shape1 + ". hash = " + shape1.hashCode)
    for (shape2 <- shapesList) {
      println("Shape2: " + shape2 + ". 1 == 2 ? " + (shape1 == shape2))
    }

    println

    // make use of "unapply()" in case matching
    def matchOn(shape: Shape) = shape match {
      case Circle(center, radius) =>
        println("Circle: center = "+center+", radius = "+radius)
      case Rectangle(ll, h, w) =>
        println("Rectangle: lower-left = "+ll+", height = "+h+", width = "+w)
      case Triangle(p1, p2, p3) => //identical to "case p1 Triangle (p2, p3) =>"
        println("Triangle: point1 = "+p1+", point2 = "+p2+", point3 = "+p3)
      case _ =>
        println("Unknown shape!"+shape)
    }

    shapesList.foreach { shape => matchOn(shape) }
    //shapesList.foreach { _ => matchOn(_) } //not working

  }

  def testCompanionMain = {
    class C1

    object C1 {
      def main(args: Array[String]) = {
        println("Companion C1 object main() is being supported!")
      }
    }

    C1.main(Array())
  }

  def testUnapplySeq = {
    val list = List("Jeff", "winnie", "Cameron")

    list match {
      case List(dad, mom, son) => println(dad + " love " + mom + " and love " + son)
      case _ => None
    }
  }
  
  def testCompanionUnapply = {
    abstract class Widget
    class Button(val label: String) extends Widget {
      var label2 = "label2"
    }
    object Button {
      def unapply(button: Button) = Some(button.label)
    }

    class TextField(val text: String) extends Widget {
      var text2 = "text2"
    }
    object TextField {
      def unapply(textField: TextField) = Some(textField.text)
    }

    val button = new Button("click me")
    button.label2 = "label2_changed"
    button match {
      //does it very much look as same as RegEx matching, it is equivalent
      //to Button.unapply(button) => button.label
      //if no companion object the statement "Button(label)" will compile err
      case Button(label) => println("matched: " + label)
      case TextField(text) => println("matched: " + text)
      case _ => None
    }
  }

  /**
   * it is allowed because "class" & "object" using different namespaces
   */
  def testCompanionApply = {
    abstract class Widget {
      def draw()
      override def toString = "(Widget)"
    }

    class Button(val label: String) extends Widget {
      def draw() {}
      override def toString = "(Button: " + label + ")"
    }
    class TextField(text: String) extends Widget {
      def draw() {}
      override def toString = "(TextField: " + text + ")"
    }

    object Widget {
      val ButtonExtractorRE = """\(button: label=([^,]+)\)""".r
      val TextFieldExtractorRE = """\(textfield: text=([^,]+)\)""".r

      def apply(specification: String): Option[Widget] = specification match {
        case ButtonExtractorRE(label) => new Some(new Button(label))
        case TextFieldExtractorRE(text) => new Some(new TextField(text))
        case _ => None
      }
    }

    val button = Widget("(button: label=I am button)")
    println(button.toString)
    val txtField = Widget("(textfield: text=I am textField)")
    println(txtField.toString)

    //more common example
    val list = List("Jeff", "Winnie", "Cameron")
    println(list(2)) //list(2) <==> list.apply(2)
  }

  def testMemberNamespace = {
    class C1(seed: Int) {
      def count = 2 * seed
      //val count = 0 // ERR, where valued member and method within the same namespace
      //object count // same as above
    }

    class C2(seed: Int) extends C1(seed: Int) {
      override val count = 101 //subclass can override super's method by val(of course, with the same name)
    }

    println("C2 count: " + new C2(5).count)
  }

  def testAbstractSubject = {
    //improved example of chapter4, artifacts are in AbstractSubjects.scala

    //try "receiveUpdate()" observer
    val button = new ButtonWithCallback with ObservableClicks with Subject4ReceiveUpdate
    val counter = new SelectiveCounter
    button.addObserver(counter)
    for (i <- 1 to 5) button.click
    println("Button clicked [" + counter.count + "] times")

    //try functional observer
    val adButton = new ButtonWithCallback with ObservableClicks with Subject4Functional
    var count = 0
    adButton.addObserver((adButton) => count += 1) //observer is a function instead of object
    for (i <- 1 to 3) adButton.click
    println("Button clicked [" + count + "] times")
  }

  def testOverrideFieldsOfAbstractParent = {
    abstract class AbstractC1 {
      //in an abstract class, field without default value is considered abstract
      val name: String
      val count: Int
    }

    class ClassWithAbstractC1 extends AbstractC1 {
      //override from abstract field no need prepend "override"
      val name = "ClassWithAbstractC1"
      val count = 1
    }

    println(new ClassWithAbstractC1 name)
  }

  def testOverrideFieldsOfConcreteParent = {
    class C1 {
      val name = "C1"
      var count = 0
    }

    class ClassWithC1 extends C1 {
      //override field of "val" must prepend "override"
      //since it is special than "var"
      override val name = "ClassWithC1" 
      count = 1
    }

    val c = new ClassWithC1()
    println(c.name)
    println(c.count)
  }

  def testLazyVal = {
    trait AbstractT2 {
      println("In T2:")
      val value: Int
      val inverse = 1.0/value
      println("AbstractT2: value=" + value + ", inverse=" + inverse)
    }

    //carefully compare c2c & c2c_wrong,
    //where both of them instantiated as anonymous class object
    val c2c = new {
      val value = 10
    } with AbstractT2

    val c2c_wrong = new AbstractT2 {
      val value = 10
    }

    println("c2c: value=" + c2c.value + ", inverse=" + c2c.inverse)
    println("c2c_wrong: value=" + c2c_wrong.value + ", inverse=" + c2c_wrong.inverse)
  }
}
